package com.ruoyi.framework.security.service

import com.ruoyi.common.constant.*
import com.ruoyi.common.exception.ServiceException
import com.ruoyi.common.exception.user.CaptchaException
import com.ruoyi.common.utils.*
import com.ruoyi.common.utils.ip.IpUtils
import com.ruoyi.framework.manager.AsyncManager
import com.ruoyi.framework.manager.factory.AsyncFactory
import com.ruoyi.framework.redis.RedisCache
import com.ruoyi.framework.security.LoginUser
import com.ruoyi.framework.security.context.AuthenticationContextHolder
import com.ruoyi.framework.security.service.*
import com.ruoyi.project.system.domain.SysUser
import com.ruoyi.project.system.service.ISysConfigService
import com.ruoyi.project.system.service.ISysUserService
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.security.authentication.AuthenticationManager
import org.springframework.security.authentication.BadCredentialsException
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken
import org.springframework.security.core.Authentication
import org.springframework.stereotype.Component
import javax.annotation.Resource

/**
 * 登录校验方法
 *
 * @author ruoyi
 */
@Component
class SysLoginService {
    @Autowired
    private val tokenService: TokenService? = null

    @Resource
    private val authenticationManager: AuthenticationManager? = null

    @Autowired
    private val redisCache: RedisCache? = null

    @Autowired
    private val userService: ISysUserService? = null

    @Autowired
    private val configService: ISysConfigService? = null

    /**
     * 登录验证
     *
     * @param username 用户名
     * @param password 密码
     * @param code 验证码
     * @param uuid 唯一标识
     * @return 结果
     */
    fun login(username: String?, password: String?, code: String?, uuid: String?): String {
        val captchaEnabled = configService!!.selectCaptchaEnabled()
        // 验证码开关
        if (captchaEnabled) {
            validateCaptcha(username, code!!, uuid!!)
        }
        // 用户验证
        var authentication: Authentication? = null
        authentication = try {
            val authenticationToken = UsernamePasswordAuthenticationToken(username, password)
            AuthenticationContextHolder.setContext(authenticationToken)
            // 该方法会去调用UserDetailsServiceImpl.loadUserByUsername
            authenticationManager!!.authenticate(authenticationToken)
        } catch (e: Exception) {
            if (e is BadCredentialsException) {
                AsyncManager.me().execute(
                    AsyncFactory.recordLogininfor(
                        username,
                        Constants.LOGIN_FAIL,
                        MessageUtils.message("user.password.not.match")
                    )
                )
                throw com.ruoyi.common.exception.user.UserPasswordNotMatchException()
            } else {
                AsyncManager.me()
                    .execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, e.message))
                throw ServiceException(e.message)
            }
        } finally {
            AuthenticationContextHolder.clearContext()
        }
        AsyncManager.me().execute(
            AsyncFactory.recordLogininfor(
                username,
                Constants.LOGIN_SUCCESS,
                MessageUtils.message("user.login.success")
            )
        )
        val loginUser = authentication!!.principal as LoginUser
        recordLoginInfo(loginUser.userId)
        // 生成token
        return tokenService!!.createToken(loginUser)
    }

    /**
     * 校验验证码
     *
     * @param username 用户名
     * @param code 验证码
     * @param uuid 唯一标识
     * @return 结果
     */
    fun validateCaptcha(username: String?, code: String, uuid: String) {
        val verifyKey = CacheConstants.CAPTCHA_CODE_KEY + StringUtils.nvl(uuid, "")
        val captcha = redisCache!!.getCacheObject(verifyKey)
        redisCache.deleteObject(verifyKey)
        when {
            captcha == null -> {
                AsyncManager.me().execute(
                    AsyncFactory.recordLogininfor(
                        username,
                        Constants.LOGIN_FAIL,
                        MessageUtils.message("user.jcaptcha.expire")
                    )
                )
                throw com.ruoyi.common.exception.user.CaptchaExpireException()
            }
            code != captcha -> {
                AsyncManager.me().execute(
                    AsyncFactory.recordLogininfor(
                        username,
                        Constants.LOGIN_FAIL,
                        MessageUtils.message("user.jcaptcha.error")
                    )
                )
                throw CaptchaException()
            }
        }
    }

    /**
     * 记录登录信息
     *
     * @param userId 用户ID
     */
    fun recordLoginInfo(userId: Long?) {
        val sysUser = SysUser()
        sysUser.userId = userId
        sysUser.loginIp = IpUtils.getIpAddr(ServletUtils.request)
        sysUser.loginDate = DateUtils.nowDate
        userService!!.updateUserProfile(sysUser)
    }
}
